#pragma once
#include "../message-queue/src/client/Connection.hpp"
#include "../common/util.hpp"
#include "../common/httplib.h"
#include "../common/Log.hpp"
#include "oj_model.hpp"
#include "oj_view.hpp"
#include <iostream>
#include <string>
#include <fstream>
#include <system_error>
#include <vector>
#include <algorithm>
#include <mutex>
#include <cassert>
#include <jsoncpp/json/json.h>
#include <hiredis/hiredis.h>
using namespace std;

// 提供服务的主机
class Machine
{
public:
    string ip;       //编译服务的ip
    int port;        //编译服务的port
    uint64_t load;   //编译服务的负载
    mutex *mtx;      // mutex禁止拷贝的，使用指针
public:
    Machine() : ip(""), port(0), load(0), mtx(nullptr)
    {
    }
    ~Machine()
    {
    }

public:
    // 提升主机负载
    void IncLoad()
    {
        if (mtx) 
            mtx->lock();
        ++load;
        if (mtx) 
            mtx->unlock();
    }

    // 减少主机负载
    void DecLoad()
    {
        if (mtx) 
            mtx->lock();
        --load;
        if (mtx) 
            mtx->unlock();
    }

    void ResetLoad()
    {
        if(mtx)
            mtx->lock();
        load = 0;
        if(mtx)
            mtx->unlock();
    }

    // 获取主机负载
    uint64_t Load()
    {
        uint64_t _load = 0;
        if (mtx) 
            mtx->lock();
        _load = load;
        if (mtx) 
            mtx->unlock();
        return _load;
    }
};

const string service_machine = "./conf/service_machine.conf";

// 消息队列管理模块，设计为单例模式
class OJMessageQueueManager
{
private:
    static once_flag initFlag; // 单例初始化标志
    AsyncWorker::ptr awp;
    Connection::ptr conn;
    Channel::ptr channel;
private:
    OJMessageQueueManager()
    {
        // 1. 实例化异步工作线程对象
        awp = make_shared<AsyncWorker>();
        // 2. 实例化连接对象
        conn = make_shared<Connection>("127.0.0.1", 8085, awp);
        // 3. 通过连接创建信道
        channel = conn->openChannel();
        // 4. 通过信道提供的服务完成所需
        //  1. 声明一个交换机exchange, 交换机类型为广播模式
        google::protobuf::Map<string, string> tmp_map;
        channel->declareExchange("exchange", MessageQueue::ExchangeType::TOPIC, true, false, tmp_map);
        //  2. 声明一个队列queue
        channel->declareQueue("queue", true, false, false, tmp_map);
        //  3. 绑定queue-exchange，且binding_key设置为queue
        channel->queueBind("exchange", "queue", "queue");
    }

    bool init()
    {
        // 1. 实例化异步工作线程对象
        awp = make_shared<AsyncWorker>();
        // 2. 实例化连接对象
        conn = make_shared<Connection>("127.0.0.1", 8085, awp);
        // 3. 通过连接创建信道
        channel = conn->openChannel();
        // 4. 通过信道提供的服务完成所需
        //  1. 声明一个交换机exchange, 交换机类型为广播模式
        google::protobuf::Map<string, string> tmp_map;
        channel->declareExchange("exchange", MessageQueue::ExchangeType::TOPIC, true, false, tmp_map);
        //  2. 声明一个队列queue
        channel->declareQueue("queue", true, false, false, tmp_map);
        //  3. 绑定queue-exchange，且binding_key设置为queue
        channel->queueBind("exchange", "queue", "queue");
        return true;
    }

    // 防止拷贝构造和赋值操作
    OJMessageQueueManager(const OJMessageQueueManager&) = delete;
    OJMessageQueueManager& operator=(const OJMessageQueueManager&) = delete;
public:
    // 获取单例实例的接口
    static OJMessageQueueManager& getInstance()
    {
        static OJMessageQueueManager instance; // 静态局部变量，保证只会初始化一次
        return instance;
    }

    ~OJMessageQueueManager()
    {
        conn->closeChannel(channel);
    }

    void publishMessage(const string& taskid, const string& message, string* resp)
    {
        // 向交换机发布消息
        MessageQueue::BasicProperties bp;
        bp.set_id(taskid);
        bp.set_delivery_mode(MessageQueue::DeliveryMode::DURABLE);
        bp.set_routing_key("queue");
        channel->basicPublish("exchange", &bp, message);

        // 发布了消息，就该等待从Redis中把消息取回来
        waitForResult(taskid, resp);
    }

    // 等待从Redis中把数据取回来
    void waitForResult(const string& taskid, string* resp) 
    {
        redisContext* ctx = redisConnect("localhost", 6379);
        if (ctx == NULL || ctx->err) 
        {
            lg(LOG_ERROR_CODE, "无法连接到Redis: %s", ctx->errstr);
            return;
        }

        string channel = taskid; // 假设使用任务ID作为频道名
        redisReply* reply;

        // 使用BRPOP命令阻塞等待结果，这里的list_key是假设的，实际应根据你的设计来
        reply = (redisReply*)redisCommand(ctx, "BRPOP %s 5", channel.c_str());
        if (reply != NULL) 
        {
            if (reply->type == REDIS_REPLY_ARRAY && reply->elements == 2) 
            {
                string result = string(reply->element[1]->str, reply->element[1]->len);
                *resp = result;
            }
            else if (reply->type == REDIS_REPLY_NIL) 
            {
                lg(Warning, "超时了, 请检查是否有异常");
            } 
            else 
            {
                lg(LOG_ERROR_CODE, "出现了未知错误");
            }
            freeReplyObject(reply);
        } 
        else
        {
            lg(LOG_ERROR_CODE, "执行BRPOP语句失败");
        }

        // freeReplyObject(reply);
        redisFree(ctx);
    }
};

// 初始化静态成员
once_flag OJMessageQueueManager::initFlag;

// 这是我们的核心业务逻辑的控制器
class Control
{
private:
    Model model_; //提供后台数据
    View view_;   //提供html渲染功能
    // LoadBlance load_blance_; //核心负载均衡器
public:
    Control()
    {
    }
    ~Control()
    {
    }

public:
    void RecoveryMachine()
    {
        // load_blance_.OnlineMachine();
    }
    //根据题目数据构建网页
    // html: 输出型参数
    bool AllQuestions(string *html)
    {
        bool ret = true;
        vector<struct Question> all;
        if (model_.GetAllQuestions(&all))
        {
            sort(all.begin(), all.end(), [](const struct Question &q1, const struct Question &q2){
                return atoi(q1.number.c_str()) < atoi(q2.number.c_str());
            });
            // 获取题目信息成功，将所有的题目数据构建成网页
            view_.AllExpandHtml(all, html);
        }
        else
        {
            *html = "获取题目失败, 形成题目列表失败";
            ret = false;
        }
        return ret;
    }
    bool Question(const string &number, string *html)
    {
        bool ret = true;
        struct Question q;
        if (model_.GetOneQuestion(number, &q))
        {
            // 获取指定题目信息成功，将所有的题目数据构建成网页
            view_.OneExpandHtml(q, html);
        }
        else
        {
            *html = "指定题目: " + number + " 不存在!";
            ret = false;
        }
        return ret;
    }
    // code: #include...
    // input: ""
    void Judge(const string &number, const string in_json, string *out_json)
    {   
        // 0. 根据题目编号，直接拿到对应的题目细节
        struct Question q;
        model_.GetOneQuestion(number, &q);

        // 1. in_json进行反序列化，得到题目的id，得到用户提交源代码，input
        Json::Reader reader;
        Json::Value in_value;
        reader.parse(in_json, in_value);
        string code = in_value["code"].asString();

        // 2. 重新拼接用户代码+测试用例代码，形成新的代码，同时生成任务ID
        int num = 1;
        Json::Value compile_value;
        string taskid = to_string(num++);
        compile_value["taskid"] = taskid;
        compile_value["input"] = in_value["input"].asString();
        compile_value["code"] = code + "\n" + q.tail;
        compile_value["cpu_limit"] = q.cpu_limit;
        compile_value["mem_limit"] = q.mem_limit;
        Json::FastWriter writer;
        string compile_string = writer.write(compile_value);
    
        // 4. 通过消息队列把信息发出去，并且等待响应
        OJMessageQueueManager::getInstance().publishMessage(taskid, compile_string, out_json);
    }
};